﻿using System;
using System.Diagnostics;
using AltSerialize;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using BrainTechLLC.Communication;
using BrainTechLLC;
using System.Net;
using BrainTechLLC.ThreadSafeObjects;
using System.Reflection;
using BrainTechLLC.Logging;
using Mud.EmlenMudMain;

namespace UserInterface
{
    public static class GameSetup
    {
        public static ConnectionPoint _connectionPoint;
        public static GameChar[] chr = new GameChar[2];

        public static bool Initialized = false;
        public static bool DoneWithInit = false;

        public static void CheckInit()
        {
            if (Initialized)
            {
                if (DoneWithInit)
                    return;
                else
                    while (!DoneWithInit)
                        Thread.Sleep(100);
            }

            Initialized = true;

            // HEREHERE
            bool importOldGameData = true;

            Globals.CheckInitialized();

            Restriction.AddMoveRestrictions();
            UsernamesPasswords.Load();

            bool loadedWorld = true;
            Stopwatch s = new Stopwatch();
            s.Start();
            {
                if (true || !WorldMap.Instance.Restore<WorldMap>(null, out WorldMap.Instance))
                {
                    Globals.CreateTestWorld();
                    loadedWorld = false;
                }
            }
            s.Stop();
            Globals.InGameLog.Log("Load Game World Info", string.Format("Game world took {0} milliseconds to load from disk", s.ElapsedMilliseconds), LogSeverity.Information);

            // Server socket stuff
            Globals.Server.BeginListening();

            CreateTestCharacters();

            CreateBasicChannels();

            WorldMap.Instance.CollectionChanged += new NotifyCollectionChangedEventHandler(Instance_CollectionChanged);
            WorldMap.Instance.SetupNotification();

            HookupTestCharacters();

            CreateDefaultUserNamePassword();

            CreateTestFatigueState();

            TestCreateAttackState();

            Globals.SchedulingEngine.ScheduleEventToExecute(() => GameSetup.chr[0].ExecuteChangesAndActions(500), 50);
            Globals.SchedulingEngine.ScheduleEventToExecute(() => GameSetup.chr[1].ExecuteChangesAndActions(500), 50);

            // GameSetup._connectionPoint = new ConnectionPoint(1, GameSetup.chr[0].AssociatedID);

            CreateDoubleDamageInfluence();

            TestAttack();
            TestViewOfThing();

            if (!loadedWorld && importOldGameData)
            {
                LoadDataFromOldMud();
                WorldMap.Instance.PerformPostRestoreSetup();
            }

            //RearrangeMap();

            DoneWithInit = true;

            TestSerialization();
        }

        private static void HookupTestCharacters()
        {
            GameSetup.chr[0] = Everything.Find<GameChar>(TestStuff.GameChar1.AssociatedID);
            GameSetup.chr[1] = Everything.Find<GameChar>(TestStuff.GameChar2.AssociatedID);
            GameSetup.chr[0].RegisterChildrenForPropertyChangeEvents();
        }

        private static void CreateTestFatigueState()
        {
            State state2 = StateFactory.CreateFatigueState(-2);
            if (GameSetup.chr[0].States.FindStateByName(state2.StateName) == null)
                GameSetup.chr[0].States.AddState(state2, 0);
        }

        private static void CreateDefaultUserNamePassword()
        {
            UsernamePassword basic = new UsernamePassword() { UserName = "owen", Password = "owen", AssociatedIDs = new ThreadSafeListValues<long>() { TestStuff.GameChar1.AssociatedID, TestStuff.GameChar2.AssociatedID } };
            UsernamePassword found = UsernamesPasswords.Instance.DoLogin(basic);

            if (found == null)
                UsernamesPasswords.Instance.CheckUniqueAndAdd(basic);
        }

        private static void CreateBasicChannels()
        {
            Channel generalChannel = new Channel("General", "General Chat", "General Chat Channel");
            generalChannel.Register();

            Channel generalChannel2 = new Channel("Channel2", "Secondary Chat", "Secondary Chat Channel");
            generalChannel2.Register();
        }

        private static void CreateTestCharacters()
        {
            TestStuff.GameChar1.Name.Name = "Char1";
            TestStuff.GameChar2.Name.Name = "Char2";

            TestStuff.GameChar1.Name.Keywords = "char1~owen~testing1";
            TestStuff.GameChar2.Name.Keywords = "char2~joshua~testing2";
        }

        public static void TestCreateAttackState()
        {
            //State state3 = StateFactory.CreateBasicAttackState(Program.chr[0].AssociatedID, Program.chr[1].AssociatedID, 500);
            //if (Program.chr[0].States.FindStateByName(state3.StateName) == null)
            //    Program.chr[0].States.AddState(state3, 500);
        }

        private static void CreateDoubleDamageInfluence()
        {
            var v = Everything.FindAllThingsOfType<Template>();
            Template influenceTemplate1 = (from thing in v where thing.FriendlyName == "Double damage influence" select thing).FirstOrDefault();
            if (influenceTemplate1 == null)
            {
                influenceTemplate1 = new Template()
                {
                    FriendlyName = "Double damage influence",
                    TypeName = "ExternalInfluence",
                    ThingProperties = new BrainTechLLC.PropertyData(),
                    PropertiesToSet = new BrainTechLLC.PropertyData()
                    {
                        Props = new ThreadSafeLookup<string, IdentifierAndValue>(StringComparer.OrdinalIgnoreCase,
                                    new KeyValuePair<string, IdentifierAndValue>("InfluenceMethodName", new IdentifierAndValue("InfluenceMethodName", "GetDoubleDamageInfluence")),
                                    new KeyValuePair<string, IdentifierAndValue>("InfluenceTypeName", new IdentifierAndValue("InfluenceTypeName", "TestDoubleDamageInfluence")),
                                    new KeyValuePair<string, IdentifierAndValue>("InfluenceName", new IdentifierAndValue("InfluenceName", "DoubleDamage2")),
                                    new KeyValuePair<string, IdentifierAndValue>("InfluenceEvent", new IdentifierAndValue("InfluenceEvent", "Attack")))
                    }
                };
                influenceTemplate1.Register();
            }

            if (GameSetup.chr[0].InfluenceCollection.InfluencesByName["Attack"].Find((a) => a.InfluenceName == "DoubleDamage2") == null)
            {
                ExternalInfluence ei = influenceTemplate1.CreateInstance<ExternalInfluence>(null);
                GameSetup.chr[0].InfluenceCollection.Add(ei.AssociatedID);
            }
        }

        public static void TestSerialization()
        {
            //List<GameChar> gameChars = Everything.FindAllThingsOfType<GameChar>();
            //byte[] bytes = gameChars.Serialize(typeof(List<GameChar>));
            //byte[] compressed = bytes.Compress();
            //Console.Write(bytes.Length.ToString() + "/" + compressed.Length.ToString());
            //var result = (List<GameChar>)Serializer.Deserialize(bytes, typeof(List<GameChar>));
            //Console.WriteLine(result.Count);

            //List<GameLocation> locations = new List<GameLocation>(WorldMap.Instance.World.AllItems);
            //bytes = locations.Serialize(typeof(List<GameLocation>));
            //compressed = bytes.Compress();
            //Console.Write(bytes.Length.ToString() + "/" + compressed.Length.ToString());
            //var result2 = (List<GameLocation>)Serializer.Deserialize(bytes, typeof(List<GameLocation>));
            //Console.WriteLine(result2.Count);
        }

        public static void TestAttack()
        {
            //CommandFactory.Attack(null, Program.chr[0], Program.chr[1], null, null);
        }

        public static void TestViewOfThing()
        {
            //IViewOfThing view = chr[1].GetViewWhenObservedBy(chr[0], ViewType.All);
            //MessageBox.Show(view.ToString());
        }

        public static void RearrangeMap()
        {
            int x = 1000;
            int y = 1000;
            int z = 1;

            var allLocs = Everything.FindAllThingsOfType<GameLocation>();
            GameLocation location = null;
            VisitorInformation visitorInfo = new VisitorInformation();

            foreach (var loc in allLocs)
            {
                var data = loc.AddAdditionalData(visitorInfo);
                data._tempXYZ = new LocationInt(0, 0, 0);
            }

            foreach (var loc in allLocs)
            {
                if (loc.Vnum > 1000)
                {
                    location = loc;
                    break;
                }
            }

            while (true)
            {
                try
                {
                    LocationAndExitVisitor locVisitor = new LocationAndExitVisitor() { VisitorInfo = visitorInfo };
                    locVisitor.VisitingExit += new EventHandler<VisitExitEventArgs>(locVisitor_VisitingExit);
                    locVisitor.VisitingLocation += new EventHandler<VisitorEventArgs>(locVisitor_VisitingLocation);
                    locVisitor.VisitOverlap += new EventHandler<VisitOverlapEventArgs>(locVisitor_VisitOverlap);

                    locVisitor.RunVisitor(location.AssociatedID, new LocationInt(x, y, z));
                    allLocs = Everything.FindAllThingsOfType<GameLocation>();

                    if ((locVisitor.Signal & ControlSignal.Done) == ControlSignal.Done)
                    {
                        foreach (var loc in allLocs)
                        {
                            var data = loc.GetAdditional(locVisitor.VisitorInfo);

                            if (!data._tempXYZ.IsEmpty())
                                loc.XYZ = data._tempXYZ;
                        }

                        break;
                    }

                    foreach (var loc in allLocs)
                    {
                        var data = loc.GetAdditional(locVisitor.VisitorInfo);
                        data.Clear();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }


        }

        static void locVisitor_VisitOverlap(object sender, VisitOverlapEventArgs e)
        {
            try
            {
                var data = e.ConflictInfo;
                var loc = e.Location;
                var visitedFrom = e.VisitedFrom;
                var dirFrom = e.Direction;
                var revDir = e.Direction.ReverseDirection();

                GameLocation newLoc = new GameLocation()
                {
                    //XYZ = loc.XYZ,
                    AreaName = loc.AreaName,
                    Indoors = loc.Indoors,
                    Name = new NameAndDescription() { Name = "No Name" },
                    RoomFlags = loc.RoomFlags,
                    SectorType = loc.SectorType
                };

                newLoc.Register();                               
                
                LocationEdge edge = new LocationEdge()
                {
                    ToLocationId = newLoc.AssociatedID 
                };

                edge.Register();

                LocationEdge edgeBack = new LocationEdge()
                {
                    ToLocationId = visitedFrom.AssociatedID
                };

                edgeBack.Register();

                LocationEdge edgeBack2 = new LocationEdge()
                {
                    ToLocationId = newLoc.AssociatedID
                };

                edgeBack2.Register();

                newLoc.EdgeIDs[(int)dirFrom] = visitedFrom.EdgeIDs[(int)dirFrom];
                newLoc.EdgeIDs[(int)revDir] = edgeBack.AssociatedID;
                visitedFrom.EdgeIDs[(int)dirFrom] = edge.AssociatedID;

                if (loc.Edges[(int)revDir] != null)
                    loc.EdgeIDs[(int)revDir] = edgeBack2.AssociatedID;
                
                var visitor = sender as LocationAndExitVisitor;

                var newData = newLoc.AddAdditionalData(visitor.VisitorInfo);
                newData._tempXYZ = new LocationInt(0, 0, 0);

                //WorldMap.Instance.World.AddTerminatingItem(newLoc, newLoc.XYZ.X, newLoc.XYZ.Y, newLoc.XYZ.Z);
                loc.RecalculateEdges();
                newLoc.RecalculateEdges();
                visitedFrom.RecalculateEdges();

                if (data._visitedFrom == null)
                    Console.WriteLine(string.Format("Already visited {0} ({1}, from {2} at {3} ({4}))",
                                    loc.ShortName.EmptyIfNull(), loc.AssociatedID, visitedFrom.ShortName, visitedFrom.AssociatedID, dirFrom));
                else
                    Console.WriteLine(string.Format("Already visited {0} ({1}, from {2} at {3} ({4}) - originally from {5} at {6} ({7}))",
                                    loc.ShortName.EmptyIfNull(), loc.AssociatedID, visitedFrom.ShortName, visitedFrom.AssociatedID, dirFrom,
                                    data._visitedFrom.ShortName.EmptyIfNull(), data._visitedFrom.AssociatedID, data._visitedFromDirection));

                e.Signal |= ControlSignal.ForceCancel;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        static void locVisitor_VisitingLocation(object sender, VisitorEventArgs e)
        {
            //var loc = e.Location;
            //var dir = e.Direction;
            //Console.WriteLine(string.Format("Visited {0} ({1} at {2} from {3})", loc.ShortName, loc.AssociatedID, loc.XYZ, dir));
        }

        static void locVisitor_VisitingExit(object sender, VisitExitEventArgs e)
        {

        }

        public static void LoadDataFromOldMud()
        {
            EmlenMud mud = new EmlenMud();
            mud.Bootup();

            for (int n = 0; n < EmlenMud.WorldTemplates.AllAreas.Count; n++)
            {
                var area = EmlenMud.WorldTemplates.AllAreas[n];
                // TODO - check for duplicates
                Area newArea = area.ToArea();
            }

            foreach (var kvp in EmlenMud.WorldTemplates.htRooms)
            {
                var room = kvp.Value;
                // TODO
                // Blech
                int x = room.VnumRoom + 100;
                int y = room.VnumRoom;
                int z = 1;
                while (WorldMap.Instance.World[x, y, z] != null)
                {
                    x+=10;
                }
                GameLocation newLoc = room.CreateGameLocation(x, y, z);
                WorldMap.Instance.World.SetValue(newLoc, x, y, z);
                Area area = Area.FindArea(room.Areastring);
                area.Rooms.Add(newLoc.AssociatedID);
            }

            foreach (var kvp in EmlenMud.WorldTemplates.AllMobPrototypes)
            {
                MobPrototype mp = kvp.Value;
                // TODO - check for duplicates
                GameCharTemplate template = mp.ToCharTemplate();
            }

            foreach (var kvp in EmlenMud.WorldTemplates.AllObjPrototypes)
            {
                ObjectPrototype op = kvp.Value;
                // TODO - check for duplicates
                op.ToItemTemplate();
            }

            try
            {
                var charTemplates = Everything.FindAllThingsOfType<GameCharTemplate>();
                Dictionary<int, GameCharTemplate> dict = new Dictionary<int, GameCharTemplate>();
                foreach (var t in charTemplates)
                {
                    dict.Add(t.ThingProperties.GetArgument<int>("Vnum"), t);
                }

                var itemTemplates = Everything.FindAllThingsOfType<ItemTemplate>();
                Dictionary<int, ItemTemplate> dictItems = new Dictionary<int, ItemTemplate>();
                foreach (var t in itemTemplates)
                {
                    dictItems.Add(t.ThingProperties.GetArgument<int>("Vnum"), t);
                }

                var allLocs = WorldMap.Instance.World.AllItems;
                Dictionary<int, GameLocation> dict2 = new Dictionary<int, GameLocation>();
                foreach (var loc in allLocs)
                {
                    if (loc.Vnum > 0)
                        dict2.Add(loc.Vnum, loc);
                }

                foreach (var list in EmlenMud.DynamicWorld.AllChars1.AllItems)
                {
                    foreach (var chr in list)
                    {
                        // TODO - check for duplicates
                        chr.ToGameChar(dict, dict2);
                    }
                }

                foreach (var loc in allLocs)
                {
                    if (loc.Edges != null)
                    {
                        for (int n = 0; n < loc.Edges.Length; n++)
                        {
                            var edge = loc.Edges[n];

                            if (edge != null)
                            {
                                if (edge.Vnum > 0)
                                {
                                    GameLocation l = GameLocation.FindLocationByVnum(edge.Vnum, dict2);

                                    if (l != null)
                                    {
                                        edge.ToLocationId = l.AssociatedID;
                                        loc.EdgeIDs[n] = edge.AssociatedID;
                                    }
                                }
                            }
                        }

                        loc.RecalculateEdges();
                    }
                }

                var dict3 = ItemTemplate.ReadItemTemplatesByVnum();

                foreach (var obj in EmlenMud.DynamicWorld.AllObjects1)
                {
                    Item i = obj.ToItem(dict3);
                }
                foreach (var loc in WorldMap.Instance.World.AllItems)
                {
                    if (loc.HasResets)
                    {
                        foreach (var reset in loc.Resets.ReadItems<Reset>())
                        {
                            ItemTemplate itemTemplate;
                            GameCharTemplate charTemplate;

                            if (dictItems.TryGetValue(reset.ResetVnum, out itemTemplate))
                            {
                                reset.PopsAssociatedID = itemTemplate.AssociatedID;
                            }
                            else if (dict.TryGetValue(reset.ResetVnum, out charTemplate))
                            {
                                reset.PopsAssociatedID = charTemplate.AssociatedID;
                            }
                            else
                            {
                                GlobalLogger.Log("Bad reset", "Could not find vnum " + reset.ResetVnum.ToString() + " to pop", LogSeverity.Warning);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
            }
        }

        static void Instance_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (Program.TheForm != null)
            {
                if (e.OldItems != null)
                    Program.TheForm.AppendOutput(e.Action.ToString() + " " + e.OldItems.Grab<long>().ToString());
                if (e.NewItems != null)
                    Program.TheForm.AppendOutput(e.Action.ToString() + " " + e.NewItems.Grab<long>().ToString());
            }
        }
    }

    public static class Program
    {
        public static Form1 TheForm;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            TheForm = new Form1();

            //PluginHelper p = new PluginHelper();
            //p.LoadPlugins(Application.StartupPath + System.IO.Path.DirectorySeparatorChar);
            //Assembly[] asms = p.LoadedAppDomains["BrainTechLLC.Core.dll"].GetAssemblies();
            //Assembly assem = asms.Find((a) => a.FullName.StartsWith("BrainTechLLC.Core"));
            //Type[] tt = assem.GetTypes();
            //Console.WriteLine(p.LoadedAppDomains.Count);
            //Console.WriteLine(tt.Length.ToString());
            //p.UnloadDomain(Application.StartupPath + System.IO.Path.DirectorySeparatorChar, "BrainTechLLC.Core.dll");
            try
            {
                Application.Run(TheForm);
            }
            finally
            {
                Globals.Server.ShutdownServer();
            }
        }
    }
}